package Element;

import java.util.*;
import java.io.*;
import java.text.NumberFormat;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.AffineTransform;
import javax.swing.*;

import BackGround.*;
import Element.Geom.*;
import DistSys.*;
import Engines.EngineProcessor;

public abstract class AbsElem implements Cloneable {
    public static final String sEOL = SysConst.sEndOfLine;

    public static final int DATA_NONE             =  -1; //None
    public static final int DATA_FONT_SIZE        =  0;
    public static final int DATA_CONST_CURR       =  1;
    public static final int DATA_CONST_POWER      =  2;
    public static final int DATA_CAPITAL_COST     =  3;
    public static final int DATA_COST_PER_SAG     =  4;
    public static final int DATA_COST_PER_INT_KW  =  5;
    public static final int DATA_COST_PER_INT_KWH =  6;
    public static final int DATA_VOLT_SAG_LEVEL   =  7;
    public static final int DATA_CUSTOMERS        =  8;
    public static final int DATA_IN_OPT_LIB       =  9;
    public static final int DATA_INST_BLOCKED     = 10;
    public static final int DATA_IS_LEADING       = 11;
    public static final int DATA_LAYER            = 12;
    public static final int DATA_LENGTH           = 13;
    public static final int DATA_MAINT_DUR        = 14;
    public static final int DATA_MAINT_RATE       = 15;
    public static final int DATA_MTTR             = 16;
    public static final int DATA_MTTS             = 17;
    public static final int DATA_NORM_OPEN        = 18;
    public static final int DATA_NUM_BOOST        = 19;
    public static final int DATA_NUM_BUCK         = 20;
    public static final int DATA_OM_COST          = 21;
    public static final int DATA_OPEN_CKT         = 22;
    public static final int DATA_OPER_FAIL        = 23;
    public static final int DATA_OPTIMIZABLE      = 24;
    public static final int DATA_PHASE            = 25;
    public static final int DATA_PF_CONST_CURR    = 26;
    public static final int DATA_PF_CONST_POWER   = 27;
    public static final int DATA_RATED_CURR       = 28;
    public static final int DATA_RATED_POWER      = 29;
    public static final int DATA_REACTANCE        = 30;
    public static final int DATA_RECLOSING        = 31;
    public static final int DATA_RESISTANCE       = 32;
    public static final int DATA_SHUNT_COND       = 33;
    public static final int DATA_SHUNT_SUCEP      = 34;
    public static final int DATA_SRC_VOLT         = 35;
    public static final int DATA_STUDY_AREA       = 36;
    public static final int DATA_SUST_FAULT       = 37;
    public static final int DATA_BOOST_TRIG       = 38;
    public static final int DATA_BUCK_TRIG        = 39;
    public static final int DATA_TAP_RATIO        = 40;
    public static final int DATA_TEMP_FAULT       = 41;
    public static final int DATA_TXFM_RATIO       = 42;
    public static final int DATA_UPGRADE_COST     = 43;
    public static final int DATA_YEAR             = 44;
    public static final int DATA_ZONE             = 45;
    public static final int DATA_ZONE_BUFFER0     = 46;
    public static final int DATA_ZONE_BUFFER2     = 47;
    public static final int DATA_ZONE_BUFFER4     = 48;
    public static final int DATA_ZONE_BUFFER1     = 49;
    public static final int DATA_ZONE_BUFFER3     = 50;
    //added by Yanli, Generator Data
    public static final int DATA_GEN_BUS = 51;
    public static final int DATA_GEN_PG = 52;
    public static final int DATA_GEN_QG = 53;
    public static final int DATA_GEN_QMAX = 54;
    public static final int DATA_GEN_QMIN = 55;
    public static final int DATA_GEN_VG = 56;
    public static final int DATA_GEN_MBASE = 57;
    public static final int DATA_GEN_STATUS = 58;
    public static final int DATA_GEN_PMAX = 59;
    public static final int DATA_GEN_PMIN = 60;
    public static final int DATA_GEN_BID = 61;

    public static final int NUM_DATA_FIELD        = 62; //TOTAL # of Data fields
    //
    public static final int RES_NONE            = -1; //None
    public static final int RES_BLOCK_OPER      =  0;
    public static final int RES_CAPA_CONS       =  1;
    public static final int RES_CAPA_CONS_KVA   =  2;
    public static final int RES_CAPITAL_DELIV   =  3;
    public static final int RES_VOLT_SAGS       =  4;
    public static final int RES_VOLT_SAGS_NODE1 =  5;
    public static final int RES_CURR_ANG        =  6;
    public static final int RES_CURR_MAG        =  7;
    public static final int RES_LOAD_LOSS       =  8;
    public static final int RES_CURR_LOADING    =  9;
    public static final int RES_KVA_RATING      = 10;
    public static final int RES_MAIFI           = 11;
    public static final int RES_MOMENT_INT      = 12;
    public static final int RES_NET_DELIV_COST  = 13;
    public static final int RES_NO_LOAD_LOSS    = 14;
    public static final int RES_NOMINAL_VOLT    = 15;
    public static final int RES_OM_DELIV        = 16;
    public static final int RES_OUT_DUR         = 17;
    public static final int RES_PF              = 18;
    public static final int RES_KVA_FLOW        = 19;
    public static final int RES_QUERY_SCORE     = 20;
    public static final int RES_QUERY_SCORE_PERC= 21;
    public static final int RES_RECLOSE_OPER    = 22;
    public static final int RES_REVERSED_FLOW   = 23;
    public static final int RES_SAG             = 24;
    public static final int RES_SAIDI           = 25;
    public static final int RES_SAIFI           = 26;
    public static final int RES_SHORTCIRC_CURR  = 27;
    public static final int RES_SUST_INT        = 28;
    public static final int RES_SWT_OPER        = 29;
    public static final int RES_TAP_POS         = 30;
    public static final int RES_TRIP_OPER       = 31;
    public static final int RES_VOLT_MAG        = 32;
    public static final int RES_VOLT_MAG_NODE1  = 33;
    public static final int RES_VOLT_ANG        = 34;
    public static final int RES_VOLT_ANG_NODE1  = 35;
    public static final int RES_VOLT_DROP       = 36;
    public static final int RES_VOLT_SAGS_ON_TGT= 37; //Voltage Sag on target!!!
    public static final int RES_PROTECT_ZONE    = 38; //protection zone
    public static final int RES_SA_MOMENT_INT   = 39; //momentary interruption
    public static final int RES_SA_SUST_INT     = 40; //sustained interruption
    public static final int RES_SA_DUR_SI       = 41; //Duration of Sustained interruption
    public static final int RES_OLV_MI          = 42; //overeload violations due to
    public static final int RES_OLV_SI          = 43; //Overload violations due to sustained int.
    public static final int RES_OLV_DUR_SI      = 44; //Duration of overload due to Sus. Int.
    public static final int RES_UVV_MI          = 45; //Under Voltage violations due to
    public static final int RES_UVV_SI          = 46; //Under Voltage violations due to sustained int.
    public static final int RES_UVV_DUR_SI      = 47; //Duration of Under Voltage violations due to Sus. Int.
    public static final int NUM_RES_FIELD       = 48; //Total # of result fields

    protected double[] adData = new double[ AbsElem.NUM_DATA_FIELD ];
    protected int[]    aiFlag = new int [ AbsElem.NUM_DATA_FIELD ];

    protected double[] adResult = new double[AbsElem.NUM_RES_FIELD];

    static public final int NUM_SAG_FROM_SHTCKT =11;
    public double[] adSagNode1 = new double[NUM_SAG_FROM_SHTCKT];
    public double[] adSagNode2 = new double[NUM_SAG_FROM_SHTCKT];

    public static final int FLAG_DFT  = 0;  //last 4 bits in HEX: 0000
    public static final int FLAG_CUST = 1;  //last 4 bits in HEX: 0001
    public static final int FLAG_AUTO = 2;  //last 4 bits in HEX: 0010

    protected String sUniqueID = null;

    protected AbsElemGeom geom;   // geom object

    // For the values of objDftElem and strDftElem the following rules apply:
    //
    // objDftTemp != null -> strDftTemp = null:
    //     element is really an element and its default template exists
    // objDftTemp == null && strDftTemp != null:
    //     element is really an element but its default template doesn't exist
    // objDftTemp == null && strDftTemp == null:
    //     element is a default template
    private AbsElem objDftTemp;  // reference to default template
    private String strDftTemp;   // name of default template

    transient private String msgForLog="";   //msg in the log window

    /*public static final int[] aiNoSyncField={
        AbsElem.DATA_LENGTH,
        AbsElem.DATA_STUDY_AREA,
        AbsElem.DATA_ZONE_BUFFER0,
        AbsElem.DATA_ZONE_BUFFER1,
        AbsElem.DATA_ZONE_BUFFER2,
        AbsElem.DATA_ZONE_BUFFER3,
        AbsElem.DATA_ZONE_BUFFER4
    };*/


    public AbsElem(){ }

    //abstract methods
    abstract public Object clone();
    abstract public int getType();
    abstract protected void draw(Graphics2D g2, double dist, double theta);

    public String getUniqueID() { return sUniqueID; }
    public void   setUniqueID(String uid) {
        // Here should be a test for uniqueness!
        sUniqueID = uid;
    }

    public AbsElemGeom getElemGeomObj() { return geom; }
    public void        setElemGeomObj(AbsElemGeom g) { this.geom = g; }

    public AbsElem getDftTemp() { return objDftTemp; }
    public String getDftTempName() {
        if (objDftTemp != null)
            return objDftTemp.getElemGeomObj().getName();
        else if (strDftTemp != null)
            return strDftTemp;
        else
            return "";
    }
    public boolean setDftTemp(String name) {
        objDftTemp = Templates.mapStringToObject(name);
        if (objDftTemp != null)
            return true;
        else {
            if (name.length() > 0)
                strDftTemp = name;
            else // name is empty
                strDftTemp = null;
            return false;
        }
    }
    public void setDftTemp(AbsElem dftTemp) {
        if (dftTemp != null) {
            objDftTemp = dftTemp;
            strDftTemp = null;
        } else if (objDftTemp != null) {
            strDftTemp = objDftTemp.getElemGeomObj().getName();
            objDftTemp = null;
        }
    }
    public boolean isTemplate() {
        // see above for explanation
        return (objDftTemp == null && strDftTemp == null);
    }

    public void setMsgForLog( String s) { msgForLog = s; }
    public String getMsgForLog() { return msgForLog; }

    /**
     * ATTENTION: This method works correctly only for elements.
     * It will produce nonsense for default templates.
     */
    public String getStringForType() {
        return SysConst.ssType[SysConst.mapTypeToIndex(getType())];
    }

    //This copyTo() will be used by clone() method of its subclass
    //AbsElem can't implement Cloneable interface since it is an abstract class
    public void copyTo(AbsElem e){
        System.arraycopy(this.adData, 0, e.adData, 0, AbsElem.NUM_DATA_FIELD);
        System.arraycopy(this.aiFlag, 0, e.aiFlag, 0, AbsElem.NUM_DATA_FIELD);

        System.arraycopy(this.adResult, 0, e.adResult, 0, AbsElem.NUM_RES_FIELD);

        System.arraycopy(this.adSagNode1, 0, e.adSagNode1, 0,
            AbsElem.NUM_SAG_FROM_SHTCKT);
        System.arraycopy(this.adSagNode2, 0, e.adSagNode2, 0,
            AbsElem.NUM_SAG_FROM_SHTCKT);

        e.setDftTemp(this.getDftTempName());
    }

    public void drawText(Graphics g){
        // Check whether we need to draw
        if (  getData(AbsElem.DATA_FONT_SIZE) == 0) {
            // Zero font size
            return;
        }
        int i;
        if (!geom.getShowAnything()) {
            // Nothing to draw
            return;
        }

        int x, y;
        if (this instanceof Line || this instanceof Bus) {
            x = (geom.x1 + geom.x2) / 2;
            y = (geom.y1 + geom.y2) / 2;
        } else {
            x = (int) (geom.frame.x + 1.5 * geom.frame.width);
            y = geom.frame.y;
        }

        Font oldFont = g.getFont();
        int fontSize = (int) ( getData(AbsElem.DATA_FONT_SIZE) *
            Sys.prjInfo.dZoomFactor / 8.0 ); // 8 == default MapSF
        Font newFont = new Font("Dialog", Font.PLAIN, fontSize);
        g.setFont(newFont);

        FontRenderContext context = ((Graphics2D)g).getFontRenderContext();
        int height = (int) Math.round(
            newFont.getLineMetrics("0", context).getHeight() + 0.5);

        g.setColor(Color.black);

        if( geom.getShowName()) {
            g.drawString(geom.getName(), x, y);
            y += height;
        }
        if( geom.getShowDesc()) {
            g.drawString(geom.getDesc(), x, y);
            y += height;
        }

        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(4);
        nf.setGroupingUsed(false);

        for (int iCat = 0; iCat < AbsElemGeom.NUM_SHOW; iCat++) {
            if (geom.getShowIndex(iCat)) {
                int count = geom.getShowIndexCount(iCat);
                for (i = 0; i < count; i++) {
                    int index = geom.getShowIndex(iCat, i);
                    String s;
                    if (iCat == AbsElemGeom.SHOW_DATA) {
                        s = SysConst.getDataFieldFromValueIndex(index) + "=" +
                            nf.format(getData(index));
                    } else { // (iCat == AbsElemGeom.SHOW_RESULT)
                        s = SysConst.getResultFieldFromValueIndex(index) + "=" +
                            nf.format(getResult(index));
                    }
                    g.drawString(s, x, y);
                    y += height;
                }
            }
        }

        g.setFont(oldFont);
    }//end of drawText

    public void drawNodes(Graphics g){
        int x1=geom.x1, y1=geom.y1, x2=geom.x2, y2=geom.y2;
        int d=SysConst.NODE_SIZE ;           // diameter of node
        if( this instanceof Bus) { return; } // no nodes for Bus

        g.setColor(Color.black);
        g.fillRect(x1-d/2,y1-d/2,d,d); //starting dot
        if( geom instanceof TwoNodeElemGeom || geom instanceof GroupGeom) {
            g.fillRect(x2-d/2,y2-d/2,d,d);    //ending dot; no need to transform
        }else if( geom instanceof ThreeNodeElemGeom) {
            g.fillRect(x2-d/2,y2-d/2,d,d);
            ThreeNodeElemGeom geom3 = (ThreeNodeElemGeom)geom;
            g.fillRect(geom3.x3-d/2, geom3.y3-d/2,d,d);
        }
    }

    public void drawHighLights(Graphics g){
        int x1=geom.x1, y1=geom.y1, x2=geom.x2, y2=geom.y2;
        int d = (SysConst.NODE_SIZE + 2);        // diameter of highlighted node

        //Sys.highLightedElemList is for TroubleShooting list only
        if( !Sys.highLightedElemList.isEmpty() &&
            Sys.highLightedElemList.contains(this)){
            g.setColor(Color.black);
            g.drawRect(x1-d/2,y1-d/2,d,d);
            g.setColor(Color.green);      //In green
            g.fillRect(x1-d/2,y1-d/2,d,d);
            return;
        }

        if( Sys.prjInfo.isHighLightSelected){
            if(geom.isSelected == true){
                g.setColor(Color.black);
                g.drawRect(x1-d/2,y1-d/2,d,d);
                g.setColor(Color.yellow);
                g.fillRect(x1-d/2,y1-d/2,d,d); //starting dot
                return;
            }
        }

        if( Sys.prjInfo.isHighLightOpenPoints){
            if(geom instanceof SwtGeom && ((SwtGeom)geom).getIsOpen()==true) {
                g.setColor(Color.black);
                g.drawRect(x1-d/2,y1-d/2,d,d);
                g.setColor(Color.red);
                g.fillRect(x1-d/2,y1-d/2,d,d); //starting dot
                return;
            }
        }

        if( Sys.prjInfo.isHighLightProtection){
            if( this instanceof Recloser || this instanceof Breaker
                || this instanceof DrtBreaker || this instanceof Fuse ) {
                g.setColor(Color.black);
                g.drawRect(x1-d/2,y1-d/2,d,d);
                g.setColor(Color.magenta);
                g.fillRect(x1-d/2,y1-d/2,d,d); //starting dot
                return;
            }
        }

        if( Sys.prjInfo.isHighLightSect){
            if( (this instanceof Switch && ((SwtGeom)geom).getIsOpen()==false )||
                (this instanceof Sect && ((SwtGeom)geom).getIsOpen()==false ) ||
                (this instanceof Switch3 ) ) {
                g.setColor(Color.black);
                g.drawRect(x1-d/2,y1-d/2,d,d);
                g.setColor(Color.blue);
                g.fillRect(x1-d/2,y1-d/2,d,d); //starting dot
                return;
            }
        }

        return;
    }

    public void drawFrame(Graphics g){
        int x1=geom.x1, x2=geom.x2, y1=geom.y1, y2=geom.y2;
        Graphics2D g2 = (Graphics2D) g;
        int d=SysConst.NODE_SIZE;

        g2.setColor(Color.yellow);
        g2.fillRect(x1-d/2,y1-d/2,d,d); //starting node
        g2.fillRect(x2-d/2,y2-d/2,d,d); //ending node
        if( geom instanceof ThreeNodeElemGeom ) {
            int x3 = ((ThreeNodeElemGeom)geom).x3;
            int y3 = ((ThreeNodeElemGeom)geom).y3;
            g2.fillRect(x3-d/2,y3-d/2,d,d);
        }

        BasicStroke dftBS = (BasicStroke) g2.getStroke();
        BasicStroke newBS = new BasicStroke(3*dftBS.getLineWidth(),
                                            dftBS.getEndCap(),
                                            dftBS.getLineJoin(),
                                            dftBS.getMiterLimit(),
                                            dftBS.getDashArray(),  // customized dash
                                            dftBS.getDashPhase());
        g2.setStroke( newBS);      //update stroke
        if (     geom instanceof BusGeom ){ ;/*g2.drawLine(x1,y1,x2,y2);*/ }
        else if( geom instanceof LineGeom){ g2.drawLine(x1,y1,x2,y2); }
        else{
            g2.drawRect(geom.frame.x, geom.frame.y,
                geom.frame.width, geom.frame.height);
        }
        g2.setColor(Color.black);
        g2.setStroke(dftBS);
    }

    public void draw(Graphics g, int dx, int dy){
        if( dx !=0 || dy!=0){
            geom.x1 = geom.x1-dx;  geom.y1 = geom.y1-dy;
            geom.x2 = geom.x2-dx;  geom.y2 = geom.y2-dy;
            geom.frame.x -= dx; geom.frame.y -= dy;
            if( geom instanceof ThreeNodeElemGeom){ //transfer switch or 3-winding txfm
                ThreeNodeElemGeom g3 = (ThreeNodeElemGeom) geom;
                g3.x3 -= dx;  g3.y3 -= dy;
            }
        }

        g.setColor( this.getCurrentColor() );
        draw(g);

        if(dx !=0 || dy!=0){
            geom.x1 = geom.x1+dx;  geom.y1 = geom.y1+dy;
            geom.x2 = geom.x2+dx;  geom.y2 = geom.y2+dy;
            geom.frame.x += dx; geom.frame.y += dy;
            if( geom instanceof ThreeNodeElemGeom){ //transfer switch or 3-winding txfm
                ThreeNodeElemGeom g3 = (ThreeNodeElemGeom) geom;
                g3.x3 += dx;  g3.y3 += dy;
            }
        }
    }

    public void drawForMove(Graphics g){
        draw(g);
    }

    protected void drawSmallElem(Graphics g){
        g.drawLine(geom.x1,geom.y1, geom.x2,geom.y2);
    }

    protected void draw(Graphics g ) {
        Graphics2D g2 = (Graphics2D) g;
        int x1 = geom.x1, y1 = geom.y1, x2 = geom.x2, y2 = geom.y2;

        double dist  = Math.sqrt((double)((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)));
        // range of asin: (-pi/2, pi/2)
        double theta = Math.asin(((double)(y2 - y1)) / dist );

        if (dist <= 4) {   //too short; skip AffineTransform
            drawSmallElem(g2);
            return;
        }

        if (x2 < x1) {
            if (y2 >= y1)      { theta = Math.PI - theta;  }
            else if (y2 <  y1) { theta = -Math.PI - theta; }
        }
        // else if (x2 >= x1)
        //     do nothing, b/c in (0-90)

        AffineTransform t_save = g2.getTransform();

        try {
            draw(g2, dist, theta);
        } catch (Exception e) {
            //System.out.println(e);
        }

        g2.setTransform(t_save); // reset to previous AffineTransform

        //if (Sys.focusElem == this) { drawFrame(g2); }
        //if (Sys.prjInfo.showNodes) { drawNodes(g2); }
        //if (Sys.prjInfo.drawText){ drawText(g2); }
    }


    private String printZoneForEngines() {
        double dZoneParam;
        if (EngineProcessor.dZoneToEngine == Zone.STUDY_AREA)
            dZoneParam = (isInStudyArea() ?
                            EngineProcessor.ZONEPARAM_INSIDE :
                            EngineProcessor.ZONEPARAM_OUTSIDE);
        else if (EngineProcessor.dZoneToEngine == Zone.SELECTED_AREA)
            dZoneParam = (geom.isSelected ?
                            EngineProcessor.ZONEPARAM_INSIDE :
                            EngineProcessor.ZONEPARAM_OUTSIDE);
        else
            dZoneParam = getData(AbsElem.DATA_ZONE);
        return ("" + dZoneParam + "\r\n");
    }


    private String printTypeForNetworkEngines(){
        String s;
        if( this instanceof DrtBreaker || this instanceof Breaker ) {
            if( this.getHasReclosingRelay() ) {
                s = SysConst.ssTypeEngine[SysConst.TYPE_RECLOSER];
            } else {
                s = SysConst.ssTypeEngine[SysConst.TYPE_BREAKER];
            }
        } else if( this instanceof Motor || this instanceof DG )
            s = SysConst.ssTypeEngine[SysConst.TYPE_LOAD];
        else
            s = SysConst.ssTypeEngine[getType()];
        return (s + "\r\n");
    }

    private String printTypeForRadialEngines(){
        String s;
        if (this instanceof DG || this instanceof Motor)
            s = SysConst.ssTypeEngine[SysConst.TYPE_LOAD];
        else if (geom instanceof SwtGeom) {      // for all switchables
            if (((SwtGeom)geom).getIsOpen()){    // for open switchables
                s = "Open_Switch";
            } else if (this instanceof Breaker || this instanceof DrtBreaker) {
                // for all closed switchables
                // which is Breaker OR Drawout_Breaker
                // (treated in the same way here)
                if (this.getHasReclosingRelay() == true)
                    s = SysConst.ssTypeEngine[SysConst.TYPE_RECLOSER];
                else
                    s = SysConst.ssTypeEngine[SysConst.TYPE_BREAKER];
            } else // for all closed switchables
                   // which are NOT Breaker or Drawout_Breaker
                s = SysConst.ssTypeEngine[getType()];
        } else // for all other elements
            s = SysConst.ssTypeEngine[getType()];

        return (s + "\r\n");
    }

    private String printTopoForEngines(){
        StringBuffer sb= new StringBuffer(100);
        String str1, str2;
        AbsElem p2, child;

        if( geom.parent == null ) str1="-1" + sEOL;
        else str1= (1+Sys.elemList.indexOf( geom.parent )) + sEOL;// id offset by 1

        //For transfer swt, Engines takes the "real" parent as the first parent
        if( geom instanceof ThreeNodeElemGeom ){
            p2 = ((ThreeNodeElemGeom)geom).parent2;
            if( p2 == null) str2= "-1" + sEOL;
            else str2= (1+Sys.elemList.indexOf( p2 ) ) + sEOL;//id offset by 1

            if( ((ThreeNodeElemGeom)geom).getIsSwitched()==false){
                sb.append( str1 ).append( str2 ); }
            else{ sb.append( str2 ).append( str1 ); }
        }
        else if( (geom instanceof SwtGeom) && ((SwtGeom)geom).getIsOpen() ) {
            p2= ((SwtGeom)geom).parent2;
            if( p2 == null) str2 = "-1" + sEOL;
            else str2= (1+Sys.elemList.indexOf( p2 ) ) + sEOL;//id offset by 1
            sb.append( str1 ).append( str2 );  //s += str1+str2;
        }
        else { sb.append( str1 ).append("-1").append(sEOL); } //s += str1 + "-1" + sEOL;  //parent 2 not applicable

        sb.append( geom.childList.size() ).append(sEOL); //s += geom.childList.size() + sEOL;
        for(int j=0;j<geom.childList.size();j++){//last child id is always -1
            child = (AbsElem) geom.childList.elementAt(j);
            sb.append( 1+Sys.elemList.indexOf(child) ).append(sEOL); //s += (1+Sys.elemList.indexOf(child)) + sEOL;
        }

        return sb.toString();
    }

    private String printXYForEngines(){
        int x1, y1, x2, y2;
        StringBuffer sb= new StringBuffer(60);
        if( this instanceof Switch3){
            if( ((ThreeNodeElemGeom)geom).getIsSwitched()==false ) {//unswitched
                sb.append( geom.x1Stored ).append(sEOL);
                sb.append( geom.y1Stored ).append(sEOL);
            }else {
                sb.append( geom.x2Stored ).append(sEOL);
                sb.append( geom.y2Stored ).append(sEOL);
            }
            sb.append(((ThreeNodeElemGeom)geom).x3Stored).append(sEOL);
            sb.append(((ThreeNodeElemGeom)geom).y3Stored).append(sEOL);
        }
        else {
            sb.append( geom.x1Stored ).append(sEOL);
            sb.append( geom.y1Stored ).append(sEOL);
            sb.append( geom.x2Stored ).append(sEOL);
            sb.append( geom.y2Stored ).append(sEOL);
        }
        return sb.toString();
    }

    private String printConnForEngines(){//only for conn1 & conn2, NA for conn3 now
        StringBuffer sb= new StringBuffer(100);
        int numConnectedElem;
        AbsElem elem;
        int id;
        Vector v1,v2;
        int x1, y1, x2, y2;

        if( this instanceof Switch3 ){
            if( ((ThreeNodeElemGeom)geom).getIsSwitched()==false ) {//unswitched
                v1 = geom.conn1;
                x1 = geom.x1Stored;
                y1 = geom.y1Stored;
            }
            else { //switched
                v1 = geom.conn2;
                x1 = geom.x2Stored;
                y1 = geom.y2Stored;
            }
            v2 = ((ThreeNodeElemGeom)geom).conn3;
            x2 = ((ThreeNodeElemGeom)geom).x3Stored;
            y2 = ((ThreeNodeElemGeom)geom).y3Stored;
        }
        else {
            v1 = geom.conn1;  x1 = geom.x1Stored; y1 = geom.y1Stored;
            v2 = geom.conn2;  x2 = geom.x2Stored; y2 = geom.y2Stored;
        }
        numConnectedElem = v1.size() + v2.size(); //initial size of conn. elem.

        int x, y;
        Vector v;
        for(int i=1; i<=2; i++){
            if( i==1 ){ x = x1;   y = y1;   v = v1;}
            else      { x = x2;   y = y2;   v = v2;}

            int sz = v.size();
            for( int k=0; k<sz; k++){
                elem =(AbsElem) v.get(k);
                if( elem instanceof Switch3 ){
                    ThreeNodeElemGeom gS3 = (ThreeNodeElemGeom) (elem.geom);
                    if( gS3.getIsSwitched() &&
                        x == gS3.x1Stored && y == gS3.y1Stored ){
                        numConnectedElem -= 1;
                        continue; //switched, connected at Node1
                    }
                    else if( gS3.getIsSwitched() == false &&
                        x == gS3.x2Stored && y == gS3.y2Stored ){
                        numConnectedElem -= 1;
                        continue; //unswitched, connected at Node2
                    }
                }
                id = (Sys.elemList.indexOf(elem) + 1);
                sb.append(id).append(sEOL).append(i).append(sEOL); // i <== 1 or 2
            }
        }

        String s = ""+ numConnectedElem +"\r\n" + sb;  //add total number as a header
        return s;
    }//end of printConnForEngines()


    /*================================================
    printOutForDistrelEngine()
    ================================================*/
    public String printOutForDistrelEngine(){
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(8);
        nf.setGroupingUsed(false);
        double realPower, reactivePower;
        StringBuffer sb = new StringBuffer(512);

        double length=0.0;
        if( this instanceof Line ) { length= this.getLength(); }
        else { length = 1.0; }

        sb.append( ( Sys.elemList.indexOf(this) + 1 ) ).append(sEOL);   //id offset by 1

        sb.append( printTypeForRadialEngines() );

        sb.append( printZoneForEngines() ); //old format

        sb.append( nf.format(length * getData(AbsElem.DATA_TEMP_FAULT))).append( sEOL);
        sb.append( nf.format(length * getData(AbsElem.DATA_SUST_FAULT))).append( sEOL);
        sb.append( nf.format(length * getData(AbsElem.DATA_OPEN_CKT))).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_MTTR))).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_MTTS))).append( sEOL);
        //operational success rate! not failure rate!
        sb.append( nf.format(1.0-getData(AbsElem.DATA_OPER_FAIL))).append( sEOL);

        double kVA= getData(AbsElem.DATA_CONST_POWER);
        double pf= getData(AbsElem.DATA_PF_CONST_POWER);
        realPower = kVA * pf;
        reactivePower = kVA * Math.sqrt( 1- pf*pf);
        if( getIsLeading() == true ) reactivePower *= -1.0; //because it's load

        sb.append( nf.format(realPower) ).append( sEOL);
        sb.append( nf.format(reactivePower) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_CONST_POWER))).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_RATED_POWER))).append( sEOL);

        if( getIsInstantBlocked())  { sb.append('1').append(sEOL) ;}
        else { sb.append('0').append(sEOL);}

        sb.append( nf.format( getData(AbsElem.DATA_CUSTOMERS))).append(sEOL);

        if(this instanceof Line) { sb.append(nf.format(length)).append(sEOL);}
        else { sb.append('0').append(sEOL);}

        sb.append( nf.format( getData(AbsElem.DATA_COST_PER_INT_KW))).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_COST_PER_INT_KWH))).append( sEOL);

        sb.append( nf.format(length * getData(AbsElem.DATA_CAPITAL_COST))).append( sEOL);
        sb.append( nf.format(length * getData(AbsElem.DATA_OM_COST))).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_COST_PER_SAG))).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_VOLT_SAG_LEVEL))).append( sEOL);

        sb.append( nf.format(length * getData(AbsElem.DATA_RESISTANCE))).append( sEOL);
        sb.append( nf.format(length * getData(AbsElem.DATA_REACTANCE))).append( sEOL);

        sb.append( nf.format(getData(AbsElem.DATA_TXFM_RATIO))).append( sEOL);

        if( getIsOptimizable()) { sb.append('1').append(sEOL); }
        else { sb.append('0').append(sEOL); }

        if( (this instanceof Switch3) && ((ThreeNodeElemGeom)geom).getIsSwitched()) {
            sb.append('1').append(sEOL);
        } else { sb.append('0').append(sEOL); }

        sb.append( Math.round(geom.group+1)).append( sEOL); //group id(external)=internal id +1;
        sb.append( printTopoForEngines() );

        return sb.toString();
    }//end of printOutForDistrelEngine();


    /*================================================
    printOutForNetrelEngine()
    ================================================*/
    public String printOutForNetrelEngine(){
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(8);
        nf.setGroupingUsed(false);
        StringBuffer sb = new StringBuffer(512);

        double length;
        if( this instanceof Line ) { length= this.getLength(); }
        else { length = 1.0; }

        sb.append( (1+Sys.elemList.indexOf(this) ) ).append( sEOL);  //id offset by 1
        sb.append( printTypeForNetworkEngines() );
        sb.append( printZoneForEngines() );   //old format
        sb.append( Math.round(geom.group+1)).append( sEOL); //group id(external)=internal id +1;

        sb.append( nf.format(length *  getData(AbsElem.DATA_TEMP_FAULT)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_SUST_FAULT)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_OPEN_CKT)) ).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_MTTR)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_MTTS)) ).append( sEOL);
        sb.append( nf.format(1.0- getData(AbsElem.DATA_OPER_FAIL))).append( sEOL); //operational success rate! not failure rate!
        sb.append( nf.format( getData(AbsElem.DATA_MAINT_RATE))).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_MAINT_DUR))).append( sEOL);

        if(  getIsInstantBlocked() ) { sb.append('1').append(sEOL); }
        else { sb.append('0').append(sEOL);}

        double kVA= getData(AbsElem.DATA_CONST_POWER);
        double pf= getData(AbsElem.DATA_PF_CONST_POWER);
        double realPower = kVA * pf;   //if DG, kVA is negative; if Load, kVA is positive
        double reactivePower = Math.abs(kVA) * Math.sqrt( 1- pf*pf); //sign of Q is up to next line
        if(  getIsLeading() ) reactivePower *= -1.0; //because it's load

        double kvaLoadMultiplier=1.0; //reserved for single/double phases
        sb.append( nf.format(kvaLoadMultiplier * realPower ) ).append( sEOL);
        sb.append( nf.format(kvaLoadMultiplier * reactivePower) ).append( sEOL);
        sb.append( nf.format(kvaLoadMultiplier * kVA)).append( sEOL);

        double maxKvaMultiplier=1.0;  //reserved
        sb.append( nf.format(maxKvaMultiplier * getData(AbsElem.DATA_RATED_POWER))).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_CUSTOMERS)) ).append( sEOL);

        if( this instanceof Line) {sb.append(nf.format(length)).append(sEOL);}
        else { sb.append('0').append(sEOL); }

        sb.append( nf.format( getData(AbsElem.DATA_COST_PER_INT_KW)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_COST_PER_INT_KWH)) ).append( sEOL);

        sb.append( nf.format(length *  getData(AbsElem.DATA_CAPITAL_COST)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_OM_COST)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_RESISTANCE)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_REACTANCE)) ).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_TXFM_RATIO)) ).append( sEOL);

        if( (this instanceof Switch3) && ((ThreeNodeElemGeom)geom).getIsSwitched() ) {
            sb.append( '1' ).append(sEOL);
        } else { sb.append( '0' ).append(sEOL); }

        if( geom instanceof SwtGeom && ((SwtGeom)geom).getIsOpen()) {
            sb.append( '1' ).append(sEOL);
        } else { sb.append( '0' ).append(sEOL); }

        sb.append( printConnForEngines() );

        return sb.toString();
    }//end of printOutForNetrelEngine();

    /*==================================================
    printOutForQuickFlowEngine
    ==================================================*/
    public String printOutForQuickFlowEngine(){
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(8);
        nf.setGroupingUsed(false);
        double realPower, reactivePower;
        StringBuffer sb = new StringBuffer(512);

        double length=1.0;
        if( this instanceof Line ) { length= this.getLength(); }
        else { length = 1.0; }

        sb.append( (1+Sys.elemList.indexOf(this) ) ).append( sEOL);  //id offset by 1

        sb.append( printTypeForRadialEngines() );

        sb.append( printZoneForEngines() ); //old format

        double kVA= getData(AbsElem.DATA_CONST_POWER);
        double pf= getData(AbsElem.DATA_PF_CONST_POWER);
        realPower = kVA * pf;
        reactivePower = kVA * Math.sqrt( 1- pf*pf);
        if(  getIsLeading() ) reactivePower *= -1.0; //load or DG

        sb.append( nf.format(realPower) ).append( sEOL);
        sb.append( nf.format(reactivePower) ).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_CONST_CURR)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_PF_CONST_CURR)) ).append( sEOL);

        // multipliers for unbalanced phasing
        double mcm=0.0 , zm=0.0;
        int iTmp= getPhase();
        mcm = geom.ubiMult[iTmp];
        zm = mcm * geom.ubzMult[iTmp];

        sb.append( nf.format(length *  getData(AbsElem.DATA_RESISTANCE) * zm) ).append( sEOL);  // multiplier
        sb.append( nf.format(length *  getData(AbsElem.DATA_REACTANCE) * zm) ).append( sEOL);   // multiplier
        sb.append( nf.format(length *  getData(AbsElem.DATA_SHUNT_COND)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_SHUNT_SUCEP)) ).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_TXFM_RATIO)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_TAP_RATIO)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_NUM_BOOST)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_NUM_BUCK)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_BOOST_TRIG)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_BUCK_TRIG)) ).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_SRC_VOLT)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_RATED_CURR) * mcm) ).append( sEOL);   // multiplier

        if( geom instanceof ThreeNodeElemGeom &&
            ((ThreeNodeElemGeom)geom).getIsSwitched() ){
                sb.append( '1' ).append(sEOL);
        } else{ sb.append( '0' ).append(sEOL); }

        sb.append( this.printTopoForEngines() );

        return sb.toString();
    }

    /**
     * print out for NetFlow; will be the unifited format for output to engines
     */
    public String printOutForNetFlowEngine(){
        NumberFormat nf = NumberFormat.getNumberInstance();
        nf.setMaximumFractionDigits(8);
        nf.setGroupingUsed(false);
        double realPower, reactivePower;
        StringBuffer sb = new StringBuffer(1024);

        double length=0.0;
        if( this instanceof Line ) { length= this.getLength(); }
        else { length =1.0; }

        sb.append( (1+Sys.elemList.indexOf(this) ) ).append( sEOL); //id offset by 1
        sb.append( SysConst.ssTypeEngine[getType()]).append( sEOL);   //unified format of type
        sb.append( getData(AbsElem.DATA_ZONE) ).append( sEOL);  //unified format of zone
        sb.append( Math.round( getData(AbsElem.DATA_STUDY_AREA) ) ).append( sEOL);

        if(geom.isSelected) { sb.append('1').append(sEOL); }
        else { sb.append('0').append(sEOL); }

        sb.append( Math.round(geom.group+1)).append( sEOL); //group id(external)=internal id +1;

        sb.append( nf.format(length *  getData(AbsElem.DATA_TEMP_FAULT)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_SUST_FAULT)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_OPEN_CKT)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_MTTR)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_MTTS)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_OPER_FAIL)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_VOLT_SAG_LEVEL)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_MAINT_RATE)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_MAINT_DUR)) ).append( sEOL);

        if(  getHasReclosingRelay() ) { sb.append('1').append(sEOL); }
        else { sb.append('0').append(sEOL); }

        if(  getIsInstantBlocked() )  { sb.append('1').append(sEOL); }
        else { sb.append('0').append(sEOL); }

        if(  getIsOptimizable() )     { sb.append('1').append(sEOL); }
        else { sb.append('0').append(sEOL); }

        if( this instanceof Switch3 && ((ThreeNodeElemGeom)geom).getIsSwitched()){
            sb.append('1').append(sEOL);
        } else { sb.append('0').append(sEOL); }

        if( geom instanceof SwtGeom && ((SwtGeom)geom).getIsOpen() ){
            sb.append('1').append(sEOL);
        } else { sb.append('0').append(sEOL); }

        if( this instanceof Line) { sb.append( nf.format(length) ).append( sEOL); }
        else { sb.append('0').append(sEOL); }

        sb.append( nf.format( getData(AbsElem.DATA_CUSTOMERS)) ).append( sEOL);

        // multipliers for unbalanced phasing
        double mcm=0.0 , zm=0.0;
        int iTmp= getPhase();
        mcm = geom.ubiMult[iTmp];
        zm = mcm * geom.ubzMult[iTmp];

        sb.append( nf.format( getData(AbsElem.DATA_CONST_POWER) ) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_PF_CONST_POWER)) ).append( sEOL);

        if( getIsLeading() ){ sb.append('1').append(sEOL); }
        else { sb.append('0').append(sEOL); }

        sb.append( nf.format(  getData(AbsElem.DATA_CONST_CURR) ) ).append( sEOL);
        sb.append( nf.format(  getData(AbsElem.DATA_PF_CONST_CURR)) ).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_RATED_POWER))).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_RATED_CURR) * mcm) ).append( sEOL);   // multiplier
        sb.append( nf.format(length * getData(AbsElem.DATA_RESISTANCE) * zm) ).append( sEOL);  // multiplier
        sb.append( nf.format(length * getData(AbsElem.DATA_REACTANCE) * zm) ).append( sEOL);   // multiplier
        sb.append( nf.format(length * getData(AbsElem.DATA_SHUNT_COND)) ).append( sEOL);
        sb.append( nf.format(length * getData(AbsElem.DATA_SHUNT_SUCEP)) ).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_TXFM_RATIO)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_TAP_RATIO)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_NUM_BOOST)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_NUM_BUCK)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_BOOST_TRIG)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_BUCK_TRIG)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_SRC_VOLT)) ).append( sEOL);

        sb.append( nf.format( getData(AbsElem.DATA_COST_PER_INT_KW)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_COST_PER_INT_KWH)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_CAPITAL_COST)) ).append( sEOL);
        sb.append( nf.format(length *  getData(AbsElem.DATA_OM_COST)) ).append( sEOL);
        sb.append( nf.format( getData(AbsElem.DATA_COST_PER_SAG)) ).append( sEOL);

        sb.append( this.printXYForEngines() );
        sb.append( this.printConnForEngines());

        return sb.toString();
    }// end of printOutForNetFlowEngine()


    protected Color getCurrentColor(){
        int iLayer = (int) Math.round( getData(AbsElem.DATA_LAYER) );
        iLayer += ProjectInfo.NUM_BACK_GND_LAYER;   //offset
        Layer lyr = (Layer)Sys.prjInfo.layerList.get( iLayer );
        if( lyr.getMode() == Layer.OFF ) return Color.white;
        else if( lyr.getMode() == Layer.GRAY) return Color.gray;

        if( geom.isSelected == true ) return Color.yellow;

        if( Sys.prjInfo.shadingCat == ProjectInfo.SHADE_NONE){
            //if( Sys.isRadial == true){ return Color.black;}
            //else return Color.gray;
            return Color.black;
        }
        else if( Sys.prjInfo.shadingCat == ProjectInfo.SHADE_BY_ZONE){
            double dZone = getData(AbsElem.DATA_ZONE);
            int sz=Sys.prjInfo.allZone.size();
            for(int i=0;i<sz;i++){
                Zone z = (Zone) Sys.prjInfo.allZone.elementAt(i);
                if (dZone == z.getZoneNumber()) {
                    return (Color) z.getZoneColor();
                }
            }
            return SysConst.shadingColors[0]; //unfounded
        }
        else if( Sys.prjInfo.shadingCat == ProjectInfo.SHADE_BY_STUDY_AREA){
            if(getData(AbsElem.DATA_STUDY_AREA)>0.01)
                return SysConst.shadingColors[2];
            else return SysConst.shadingColors[1];
        }
        else if (Sys.prjInfo.shadingCat == ProjectInfo.SHADE_BY_NOM_VOLTAGE){
            int i;
            double voltValue;
            for ( i=0 ; i<Sys.voltageLevels.size() ; i++) {
                voltValue = ((Double)Sys.voltageLevels.get(
                    Sys.voltageLevels.size()-1-i)).doubleValue();
                if ( Math.abs(  adResult[AbsElem.RES_NOMINAL_VOLT] - voltValue )
                    < Math.abs(Sys.DOUBLE_COMPARE*voltValue)){//are the equal?
                    break;
                }
            } // end of for
            if (i<Sys.voltageLevels.size()) {
                return SysConst.shadingColors[
                    (i % (SysConst.shadingColors.length-1)) + 1];
            } else { // for voltage level 0.0kV (disconnected elements)
                return SysConst.shadingColors[0];
            }
        } else if (Sys.prjInfo.shadingCat >= 0) {
            ShadingOption so = Sys.prjInfo.currentShadingOption;
            double  val;
            int     iIndexOfValue = so.getIndex();
            if (so.getIsResult()) {
                if( iIndexOfValue < 0 || iIndexOfValue >= AbsElem.NUM_RES_FIELD )
                    return SysConst.shadingColors[0];
                val = getResult(iIndexOfValue);
            } else {
                if( iIndexOfValue < 0 || iIndexOfValue >= AbsElem.NUM_DATA_FIELD )
                    return SysConst.shadingColors[0];
                val = getData(iIndexOfValue);
            }
            val = (val - so.getStartValue()) / so.getBinSize();
            if (val < 0.0) {
                return SysConst.shadingColors[0]; }
            else if (val >= 4.0) {
                return SysConst.shadingColors[5]; }
            else {
                if (!so.getContinuous()) {
                    // linear transformation:
                    // [0,1[ -> 1
                    //
                    return SysConst.shadingColors[(int)val+1];
                } else
                    // nonlinear transformation:
                    // 0  -0.5 -> 0-1   => 2*val
                    // 0.5-1.5 -> 1-2  \
                    // 1.5-2.5 -> 2-3   => val + 0.5
                    // 2.5-3.5 -> 3-4  /
                    // 3.5-4   -> 4-5   => 2*(val-3.5) + 4 = 2*val - 3
                    if (val < 0.5)       val *= 2;
                    else if (val <= 3.5) val += 0.5;
                    else                 val = 2*val - 3;
                    // linear interpolation between c1 and c2
                    int iVal = (int)val;
                    Color c1 = SysConst.shadingColors[iVal];
                    Color c2 = SysConst.shadingColors[iVal+1];
                    double j2 = val - iVal;
                    double j1 = 1.0 - j2;
                    return new Color((int)(c2.getRed()*j2  +c1.getRed()*j1),
                                     (int)(c2.getGreen()*j2+c1.getGreen()*j1),
                                     (int)(c2.getBlue()*j2 +c1.getBlue()*j1));
            }
        } else
            return SysConst.shadingColors[0];
    }

    /**
     * Returns whether the data field with the given index "makes sense" for
     * this element type or not.
     *
     * @param index  a <code>int</code> specifying the index.
     *
     * @return  <code>true</code> if the data field "makes sense",
     *          <code>false</code> otherwise
     */
    abstract public boolean getDataExists(int index);

    /**
     * Returns whether the result field with the given index "makes sense" for
     * this element type or not.
     *
     * @param index  a <code>int</code> specifying the index.
     *
     * @return  <code>true</code> if the result field "makes sense",
     *          <code>false</code> otherwise
     */
    abstract public boolean getResultExists(int index);

    /**
     * Divide length-sensitive data fields
     * For line/cable only
     */
    public void divideDataInPerUnit( double k) {
        if( this instanceof Line){  //Line or Cable
             setData(AbsElem.DATA_RESISTANCE,    getData(AbsElem.DATA_RESISTANCE) / k);
             setData(AbsElem.DATA_REACTANCE,     getData(AbsElem.DATA_REACTANCE) / k);
             setData(AbsElem.DATA_SHUNT_COND,    getData(AbsElem.DATA_SHUNT_COND) / k);
             setData(AbsElem.DATA_SHUNT_SUCEP,   getData(AbsElem.DATA_SHUNT_SUCEP) / k);
             setData(AbsElem.DATA_TEMP_FAULT,  getData(AbsElem.DATA_TEMP_FAULT) / k);
             setData(AbsElem.DATA_SUST_FAULT,  getData(AbsElem.DATA_SUST_FAULT) / k);
             setData(AbsElem.DATA_OPEN_CKT,    getData(AbsElem.DATA_OPEN_CKT) / k);
             setData(AbsElem.DATA_CAPITAL_COST,  getData(AbsElem.DATA_CAPITAL_COST) / k);
             setData(AbsElem.DATA_OM_COST,     getData(AbsElem.DATA_OM_COST) / k);
             setData(AbsElem.DATA_UPGRADE_COST,  getData(AbsElem.DATA_UPGRADE_COST) / k);
        }
    }//end of updateDataInPerUnitForElem();

    public void setData(int index, double value) throws DataException {
        boolean bValid = false;
        switch(index) {
            case AbsElem.DATA_LENGTH: // length is handled differently in LineGeom
            case AbsElem.DATA_ZONE:
            case AbsElem.DATA_FONT_SIZE:
            case AbsElem.DATA_ZONE_BUFFER0:
            case AbsElem.DATA_ZONE_BUFFER1:
            case AbsElem.DATA_ZONE_BUFFER2:
            case AbsElem.DATA_ZONE_BUFFER3:
            case AbsElem.DATA_ZONE_BUFFER4:
                if (value >= 0.0)  bValid = true;
                break;
            case AbsElem.DATA_PHASE:
                if (value == 0.0 || value == 1.0 ||
                    value == 2.0 || value == 3.0) bValid = true;
                break;
            case AbsElem.DATA_YEAR:
                if (value >= 0.0 && Math.ceil(value) == value)  bValid = true;
                break;
            case AbsElem.DATA_STUDY_AREA:
                if (value == 0.0 || value == 1.0)  bValid = true;
                break;
            case AbsElem.DATA_LAYER:
                if( this instanceof BgImage) {
                    if( value==ProjectInfo.BG_IMAGE_LAYER) { bValid = true; }
                } else if(this instanceof BgLine){
                    if( value==ProjectInfo.BG_LINE_LAYER)  { bValid = true; }
                } else {//other foreground elements
                    if (value>=0 && value < ProjectInfo.NUM_FORE_GND_LAYER &&
                        Math.ceil(value) == value) { bValid = true; }
                }
                break;
            case AbsElem.DATA_RESISTANCE:
            case AbsElem.DATA_REACTANCE:
            case AbsElem.DATA_SHUNT_COND:
            case AbsElem.DATA_SHUNT_SUCEP:
            case AbsElem.DATA_CONST_POWER:
            case AbsElem.DATA_CONST_CURR:
                bValid = true; // all value are valid
                break;
            case AbsElem.DATA_PF_CONST_POWER:
            case AbsElem.DATA_PF_CONST_CURR:
                if (value >= -1.0 && value <= 1.0)  bValid = true;
                break;
            case AbsElem.DATA_RATED_POWER:
            case AbsElem.DATA_RATED_CURR:
            case AbsElem.DATA_SRC_VOLT:
            case AbsElem.DATA_TXFM_RATIO:
            case AbsElem.DATA_TAP_RATIO:
            case AbsElem.DATA_BOOST_TRIG:
            case AbsElem.DATA_BUCK_TRIG:
                if (value >= 0.0)  bValid = true;
                break;
            case AbsElem.DATA_NUM_BOOST:
            case AbsElem.DATA_NUM_BUCK:
                if (value >= 0.0 && Math.ceil(value) == value)  bValid = true;
                break;
            case AbsElem.DATA_IS_LEADING:
            case AbsElem.DATA_OPTIMIZABLE:
            case AbsElem.DATA_IN_OPT_LIB:
                if (value == 0.0 || value == 1.0)  bValid = true;
                break;
            case AbsElem.DATA_CUSTOMERS:
            case AbsElem.DATA_TEMP_FAULT:
            case AbsElem.DATA_SUST_FAULT:
            case AbsElem.DATA_OPEN_CKT:
            case AbsElem.DATA_MTTR:
            case AbsElem.DATA_MTTS:
            case AbsElem.DATA_OPER_FAIL:
            case AbsElem.DATA_VOLT_SAG_LEVEL:
            case AbsElem.DATA_CAPITAL_COST:
            case AbsElem.DATA_OM_COST:
            case AbsElem.DATA_COST_PER_INT_KW:
            case AbsElem.DATA_COST_PER_INT_KWH:
            case AbsElem.DATA_COST_PER_SAG:
            case AbsElem.DATA_MAINT_RATE:
            case AbsElem.DATA_MAINT_DUR:
            case AbsElem.DATA_UPGRADE_COST:
                if (value >= 0.0)  bValid = true;
                break;
            case AbsElem.DATA_NORM_OPEN:
            case AbsElem.DATA_RECLOSING:
            case AbsElem.DATA_INST_BLOCKED:
                if (value == 0.0 || value == 1.0)  bValid = true;
                break;
            case AbsElem.DATA_GEN_BUS: //Yanli
            case AbsElem.DATA_GEN_PG: bValid = true; break;
            case AbsElem.DATA_GEN_QG: bValid = true; break;
            case AbsElem.DATA_GEN_QMAX: bValid = true; break;
            case AbsElem.DATA_GEN_QMIN: bValid = true; break;
            case AbsElem.DATA_GEN_VG: bValid = true; break;
            case AbsElem.DATA_GEN_MBASE: bValid = true; break;
            case AbsElem.DATA_GEN_STATUS: bValid = true; break;
            case AbsElem.DATA_GEN_PMAX: bValid = true; break;
            case AbsElem.DATA_GEN_PMIN: bValid = true; break;
            case AbsElem.DATA_GEN_BID: bValid = true; break;
        }
        if (bValid)
            adData[index] = value;
        else {
            throw new DataException( index, value );//SysConst.MappedIndex.GEOM, index, value);
        }
    }

    public double getData(int index) { return adData[index]; }

    public final void multiplyData( int index, double factor){
        adData[index] *= factor;
    }

    public final void addToData(int index, double value){
        adData[index] += value;
    }

    public final boolean getCustomizedFlag( int index ){
        int iLastTwoBits = aiFlag[index] & 0x00000003;  //bit pattern: 0000...0011;
        return ( iLastTwoBits == FLAG_CUST );
    }

    public final void setCustomizedFlag(int index, boolean b){
        aiFlag[index] &= 0xFFFFFFFC;  //bit pattern: 1111...1100
        if( b ) { aiFlag[index] += FLAG_CUST; } //otherwise, it takes FLAG_DFT
    }

    public final boolean getAutoFlag( int index ){
        int iLastTwoBits = aiFlag[index] & 0x00000003;  //bit pattern: 0000...0011;
        return ( iLastTwoBits == FLAG_AUTO );
    }
    public final void setAutoFlag( int index, boolean b ){
        aiFlag[index] &= 0xFFFFFFFC;  //bit pattern: 1111...1100
        if( b ) { aiFlag[index] += FLAG_AUTO; } //otherwise, it takes FLAG_DFT
    }

    public final boolean getDftFlag( int index ){
        int iLastTwoBits = aiFlag[index] & 0x00000003;  //bit pattern: 0000...0011;
        return ( iLastTwoBits == this.FLAG_DFT );
    }
    public final void setDftFlag( int index, boolean b ){
        aiFlag[index] &= 0xFFFFFFFC;  //bit pattern: 1111...1100
        if( b ) { aiFlag[index] += FLAG_DFT; }  //same since FLAG_DFT =0;
        else { aiFlag[index] += FLAG_CUST; }    //Otherwise, takes FLAG_CUST
    }

    //set/get flag should not be used except during I/O load/save
    public void   setFlag(int index, int val) { aiFlag[index] = val; }
    public int    getFlag(int index){ return aiFlag[index]; }

    public void   setResult(int index, double val){ adResult[index] = val; }
    public double getResult(int index){ return adResult[index]; }

    // special getData methods
    public double  getLength(){
        if( this instanceof Line) { return adData[AbsElem.DATA_LENGTH]; }
        else { return 1.0; }
    }

    public int getPhase() { return (int)Math.round(adData[AbsElem.DATA_PHASE]);}
    public boolean isInStudyArea(){ return ( adData[AbsElem.DATA_STUDY_AREA]>0.01); }
    public boolean getHasReclosingRelay(){  return (adData[AbsElem.DATA_RECLOSING] > 0.001); }
    public boolean getIsInstantBlocked(){   return (adData[AbsElem.DATA_INST_BLOCKED] > 0.001); }
    public boolean getIsNormOpen(){     return (adData[AbsElem.DATA_NORM_OPEN] > 0.001); }
    public boolean getIsLeading(){      return (adData[AbsElem.DATA_IS_LEADING] > 0.001); }
    public boolean getIsOptimizable(){  return (adData[AbsElem.DATA_OPTIMIZABLE] > 0.001); }
    public boolean getIsInOptLib(){     return (adData[AbsElem.DATA_IN_OPT_LIB] > 0.001); }

    public boolean isDataRelatedToLength(int iDataFieldIndex){
        if( iDataFieldIndex == AbsElem.DATA_RESISTANCE ||
            iDataFieldIndex == AbsElem.DATA_REACTANCE ||
            iDataFieldIndex == AbsElem.DATA_SHUNT_COND ||
            iDataFieldIndex == AbsElem.DATA_SHUNT_SUCEP ||
            iDataFieldIndex == AbsElem.DATA_TEMP_FAULT ||
            iDataFieldIndex == AbsElem.DATA_SUST_FAULT ||
            iDataFieldIndex == AbsElem.DATA_OPEN_CKT ||
            iDataFieldIndex == AbsElem.DATA_CAPITAL_COST ||
            iDataFieldIndex == AbsElem.DATA_OM_COST ||
            iDataFieldIndex == AbsElem.DATA_UPGRADE_COST
        ) { return true; }
        else { return false; }
    }
};
